package cn.yj.netty.message.heart;

import cn.yj.netty.support.test.MessageProtocol;
import cn.yj.netty.common.MessageType;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 永健
 * @since 2022-02-16 12:21
 */
@Component
public class HearBeatHandler implements IHeartBeatMonitor {

    private static final Logger log = LoggerFactory.getLogger(HearBeatHandler.class);

    private static Map<String, Integer> map = new ConcurrentHashMap<>();

    // 三次ping不回消息 关闭连接
    private static final Integer TIME_COUNT = 3;

    /**
     * 触发心跳机制
     * @param ctx
     * @param evt
     * @param runnable
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt, Runnable runnable) {
        if (evt instanceof IdleStateEvent) {
            IdleState state = ((IdleStateEvent) evt).state();
            switch (state) {
                case READER_IDLE:
                    Integer integer = map.getOrDefault(ctx.channel().id().asLongText(), 0);
                    if (integer >= TIME_COUNT) {
                        ctx.channel().close();
                        map.remove(ctx.channel().id().asLongText());
                        log.info("ping 客户端没有响应,关闭连接,host={}",ctx.channel().remoteAddress().toString());
                        return;
                    }
                    ++integer;
                    map.put(ctx.channel().id().asLongText(), integer);
                    // 5秒内收不到数据就ping一个
                    log.info("ping 客户端,host={}",ctx.channel().remoteAddress().toString());
                    ctx.channel().writeAndFlush(MessageProtocol.newMessageProtocolHearPack().setMessageType(MessageType.HEART_BEAT_PING));
                    break;
                default:
                    break;
            }
            return;
        }
        runnable.run();
    }

    /**
     * 读消息触发
     * @param ctx
     */
    @Override
    public void doReadTriggered(ChannelHandlerContext ctx){
        String channelId = ctx.channel().id().asLongText();
        if (map.containsKey(channelId)){
            map.remove(channelId);
        }
    }
}
